﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TokyoTyrant.NET;

namespace TokyoTyrantBenchmark {

    class Program {

        static void Main(string[] args) {

            Console.Write("Enter the server name (localhost): ");
            string host = Console.ReadLine();
            if (host == "") {
                host = "localhost";
            }
            int port = 1978;

            using (var con = new TokyoTyrantConnection()) {
                Console.WriteLine("host: " + host);
                Console.WriteLine("port: " + port.ToString());
                con.Connect(host, port);
                var status = con.GetDatabaseStatus();
                foreach (var key in status.Keys) {
                    Console.WriteLine(key + ": " + status[key]);
                }
                Console.WriteLine("");
                OutputDatabaseSize(con);
                Console.WriteLine("");
                Console.WriteLine("WARNING: This will delete all data in the target database.");
                Console.Write("Are you sure you wish to continue [y/N]?");
                var chr = Console.ReadLine();
                if (chr.ToUpper() == "Y") {

                    try {

                        Console.WriteLine("\r\nClearing database...");
                        con.Vanish();
                        OutputDatabaseSize(con);
                        Console.WriteLine("");

                        if (status["type"] == "table") {
                            TableTest(con);
                        } else {
                            HashTest(con);
                        }
                        Console.WriteLine("\r\nSynchronizing database...");
                        con.Synchronize();

                        Console.WriteLine("\r\nOptimizing database...");
                        con.Optimize("");
                        OutputDatabaseSize(con);


                    } catch (Exception ex) {
                        Console.WriteLine(ex.Message);
                    }
                    Console.WriteLine("Press [ENTER] to terminate...");
                    Console.ReadLine();

                }
            }
        }

        public static void OutputDatabaseSize(TokyoTyrantConnection con) {
            var dbsize = con.GetDatabaseSize();
            var reccnt = con.GetRecordCount();
            Console.WriteLine("Database has " + reccnt + " records [" + dbsize.ToString() + " bytes]");
        }

        public static void TableTest(TokyoTyrantConnection con) {
            Console.WriteLine("Adding index to date column...");
            con.SetIndex("date", TokyoTyrantConnection.IndexOption.ITDECIMAL);
            SendTableData(con, 1, 10000, 20);
            QueryData1(con, 2);
        }


        static void QueryData1(TokyoTyrantConnection con, int testnum) {
            double recsPerSecond = 0;
            var start = DateTime.Now;
            var qs = new DateTime(2000, 01, 01).ToInt();
            var qe = new DateTime(2000, 01, 02).ToInt();
            var qrecords = con.QueryRecords(new TokyoQuery().NumberGreaterThanOrEqual("date", qs).NumberLessThan("date", qe));
            var seconds = DateTime.Now.Subtract(start).TotalSeconds;
            var cnt = 0;
            foreach (var k in qrecords.Keys) {
                var column = qrecords[k];
                foreach (var ck in column.Keys) {
                    cnt += column[ck].Length;
                }
            }
           
            if (seconds > 0) {
                recsPerSecond = qrecords.Keys.Count / seconds;
            }
            Console.WriteLine("[" + testnum + "] Queried " + qrecords.Keys.Count + " recs (" + cnt + " bytes) in " + seconds + " secs at " + string.Format("{0:0.00}", recsPerSecond) + " recs/sec");
        }

        static void SendTableData(TokyoTyrantConnection con, int testnum, int recordCount, int columnCount) {
            double recsPerSecond = 0;
            var list = new List<Dictionary<string, string>>();
            var arglist = new List<string[]>();
            var startDate = new DateTime(2000, 01, 01);
            for (int i = 0; i < recordCount; i++) {
                var columns = new Dictionary<string, string>();
                columns.Add("date", Convert.ToString(startDate.ToInt()));
                startDate = startDate.Add(new TimeSpan(0, 0, 15));
                for (int ii = 0; ii < columnCount; ii++) {
                    columns.Add("col" + ii, string.Format("{0:0000000000}", i));
                }
                list.Add(columns);
                var args = new List<string>();
                args.Add(""); // for the key
                foreach (var k in columns.Keys) {
                    args.Add(k);
                    args.Add(columns[k]);
                }
                arglist.Add(args.ToArray());
            }

            var start = DateTime.Now;
            for (int i = 0; i < recordCount; i++) {
                var seconds = DateTime.Now.Subtract(start).TotalSeconds;
                if (seconds > 0) {
                    recsPerSecond = i / seconds;
                }

                if (i % 100 == 0) {
                    Console.Write(string.Format("{0:0000}", i) + " " + string.Format("{0:0.00}", recsPerSecond) + " records/second           ");
                    Console.CursorLeft = 0;
                }
                var key = string.Format("ID{0}{1:0000000000}", testnum, i);
                con.PutColumns(key, list[i], true);
            }
            Console.WriteLine("[" + testnum + "] Added " + recordCount + " recs with " + columnCount + " cols in " + DateTime.Now.Subtract(start).TotalSeconds + " secs at " + string.Format("{0:0.00}", recsPerSecond) + " recs/sec");
        }

        public static void HashTest(TokyoTyrantConnection con) {
            SendKeys(con, 1, 10000, false);
            SendKeys(con, 2, 10000, true);
        }

        public static void SendKeys(TokyoTyrantConnection con, int testnum, int recordCount, bool fast) {
            double recsPerSecond = 0;
            var start = DateTime.Now;
            for (int i = 0; i < recordCount; i++) {
                var seconds = DateTime.Now.Subtract(start).TotalSeconds;
                if (seconds > 0) {
                    recsPerSecond = i / seconds;
                }
                if (i % 100 == 0) {
                    Console.Write(string.Format("{0:0000}", i) + " " + string.Format("{0:0.00}", recsPerSecond) + " records/second           ");
                    Console.CursorLeft = 0;
                }
                var key = string.Format("ID{1}{0:00000}", i, testnum);

                if (!fast)
                    con.Put(key, string.Format("{0:0000000000}", i), true);
                else
                    con.PutFast(key, string.Format("{0:0000000000}", i));
            }
            Console.WriteLine("[" + testnum + "] Added " + recordCount + " recs in " + DateTime.Now.Subtract(start).TotalSeconds + " secs at " + string.Format("{0:0.00}", recsPerSecond) + " recs/sec");
        }


    }


    public static class DateTimeExt {
        public static readonly DateTime UnixTime = new DateTime(1970, 01, 01);
        public static int ToInt(this DateTime time) {
            return (int)time.Subtract(UnixTime).TotalSeconds;
        }

    }
}
